<!--
  - SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
  - SPDX-License-Identifier: AGPL-3.0-or-later
-->
<template>
	<component
		:is="tag"
		v-show="!deleted && !isLimbo"
		:class="{ 'comment--loading': loading }"
		class="comment">
		<!-- Comment header toolbar -->
		<div class="comment__side">
			<!-- Author -->
			<NcAvatar
				class="comment__avatar"
				:display-name="actorDisplayName"
				:user="actorId"
				:size="32" />
		</div>
		<div class="comment__body">
			<div class="comment__header">
				<span class="comment__author">{{ actorDisplayName }}</span>

				<!-- Comment actions,
					show if we have a message id and current user is author -->
				<NcActions v-if="isOwnComment && id && !loading" class="comment__actions">
					<template v-if="!editing">
						<NcActionButton
							close-after-click
							@click="onEdit">
							<template #icon>
								<IconPencilOutline :size="20" />
							</template>
							{{ t('comments', 'Edit comment') }}
						</NcActionButton>
						<NcActionSeparator />
						<NcActionButton
							close-after-click
							@click="onDeleteWithUndo">
							<template #icon>
								<IconTrashCanOutline :size="20" />
							</template>
							{{ t('comments', 'Delete comment') }}
						</NcActionButton>
					</template>

					<NcActionButton v-else @click="onEditCancel">
						<template #icon>
							<IconClose :size="20" />
						</template>
						{{ t('comments', 'Cancel edit') }}
					</NcActionButton>
				</NcActions>

				<!-- Show loading if we're editing or deleting, not on new ones -->
				<div v-if="id && loading" class="comment_loading icon-loading-small" />

				<!-- Relative time to the comment creation -->
				<NcDateTime
					v-else-if="creationDateTime"
					class="comment__timestamp"
					:timestamp="timestamp"
					:ignore-seconds="true" />
			</div>

			<!-- Message editor -->
			<form v-if="editor || editing" class="comment__editor" @submit.prevent>
				<div class="comment__editor-group">
					<NcRichContenteditable
						ref="editor"
						:auto-complete="autoComplete"
						:contenteditable="!loading"
						:label="editor ? t('comments', 'New comment') : t('comments', 'Edit comment')"
						:placeholder="t('comments', 'Write a comment …')"
						:model-value="localMessage"
						:user-data="userData"
						aria-describedby="tab-comments__editor-description"
						@update:value="updateLocalMessage"
						@submit="onSubmit" />
					<div class="comment__submit">
						<NcButton
							variant="tertiary-no-background"
							type="submit"
							:aria-label="t('comments', 'Post comment')"
							:disabled="isEmptyMessage"
							@click="onSubmit">
							<template #icon>
								<NcLoadingIcon v-if="loading" />
								<IconArrowRight v-else :size="20" />
							</template>
						</NcButton>
					</div>
				</div>
				<div id="tab-comments__editor-description" class="comment__editor-description">
					{{ t('comments', '@ for mentions, : for emoji, / for smart picker') }}
				</div>
			</form>

			<!-- Message content -->
			<NcRichText
				v-else
				class="comment__message"
				:class="{ 'comment__message--expanded': expanded }"
				:text="richContent.message"
				:arguments="richContent.mentions"
				use-markdown
				@click.native="onExpand" />
		</div>
	</component>
</template>

<script>
import { getCurrentUser } from '@nextcloud/auth'
import { translate as t } from '@nextcloud/l10n'
import { mapStores } from 'pinia'
import NcActionButton from '@nextcloud/vue/components/NcActionButton'
import NcActions from '@nextcloud/vue/components/NcActions'
import NcActionSeparator from '@nextcloud/vue/components/NcActionSeparator'
import NcAvatar from '@nextcloud/vue/components/NcAvatar'
import NcButton from '@nextcloud/vue/components/NcButton'
import NcDateTime from '@nextcloud/vue/components/NcDateTime'
import NcLoadingIcon from '@nextcloud/vue/components/NcLoadingIcon'
import NcUserBubble from '@nextcloud/vue/components/NcUserBubble'
import IconArrowRight from 'vue-material-design-icons/ArrowRight.vue'
import IconClose from 'vue-material-design-icons/Close.vue'
import IconPencilOutline from 'vue-material-design-icons/PencilOutline.vue'
import IconTrashCanOutline from 'vue-material-design-icons/TrashCanOutline.vue'
import CommentMixin from '../mixins/CommentMixin.js'
import { useDeletedCommentLimbo } from '../store/deletedCommentLimbo.js'

// Dynamic loading
const NcRichContenteditable = () => import('@nextcloud/vue/components/NcRichContenteditable')
const NcRichText = () => import('@nextcloud/vue/components/NcRichText')

export default {
	/* eslint vue/multi-word-component-names: "warn" */
	name: 'Comment',

	components: {
		IconArrowRight,
		IconClose,
		IconTrashCanOutline,
		IconPencilOutline,
		NcActionButton,
		NcActions,
		NcActionSeparator,
		NcAvatar,
		NcButton,
		NcDateTime,
		NcLoadingIcon,
		NcRichContenteditable,
		NcRichText,
	},

	mixins: [CommentMixin],

	inheritAttrs: false,

	props: {
		actorDisplayName: {
			type: String,
			required: true,
		},

		actorId: {
			type: String,
			required: true,
		},

		creationDateTime: {
			type: String,
			default: null,
		},

		/**
		 * Force the editor display
		 */
		editor: {
			type: Boolean,
			default: false,
		},

		/**
		 * Provide the autocompletion data
		 */
		autoComplete: {
			type: Function,
			required: true,
		},

		userData: {
			type: Object,
			default: () => ({}),
		},

		tag: {
			type: String,
			default: 'div',
		},
	},

	data() {
		return {
			expanded: false,
			// Only change data locally and update the original
			// parent data when the request is sent and resolved
			localMessage: '',
			submitted: false,
		}
	},

	computed: {
		...mapStores(useDeletedCommentLimbo),

		/**
		 * Is the current user the author of this comment
		 *
		 * @return {boolean}
		 */
		isOwnComment() {
			return getCurrentUser().uid === this.actorId
		},

		richContent() {
			const mentions = {}
			let message = this.localMessage

			Object.keys(this.userData).forEach((user, index) => {
				const key = `mention-${index}`
				const regex = new RegExp(`@${user}|@"${user}"`, 'g')
				message = message.replace(regex, `{${key}}`)
				mentions[key] = {
					component: NcUserBubble,
					props: {
						user,
						displayName: this.userData[user].label,
						primary: this.userData[user].primary,
					},
				}
			})

			return { mentions, message }
		},

		isEmptyMessage() {
			return !this.localMessage || this.localMessage.trim() === ''
		},

		/**
		 * Timestamp of the creation time (in ms UNIX time)
		 */
		timestamp() {
			return Date.parse(this.creationDateTime)
		},

		isLimbo() {
			return this.deletedCommentLimboStore.checkForId(this.id)
		},
	},

	watch: {
		// If the data change, update the local value
		message(message) {
			this.updateLocalMessage(message)
		},
	},

	beforeMount() {
		// Init localMessage
		this.updateLocalMessage(this.message)
	},

	methods: {
		t,

		/**
		 * Update local Message on outer change
		 *
		 * @param {string} message the message to set
		 */
		updateLocalMessage(message) {
			this.localMessage = message.toString()
			this.submitted = false
		},

		/**
		 * Dispatch message between edit and create
		 */
		onSubmit() {
			// Do not submit if message is empty
			if (this.localMessage.trim() === '') {
				return
			}

			if (this.editor) {
				this.onNewComment(this.localMessage.trim())
				this.$nextTick(() => {
					// Focus the editor again
					this.$refs.editor.$el.focus()
				})
				return
			}
			this.onEditComment(this.localMessage.trim())
		},

		onExpand() {
			this.expanded = true
		},
	},

}
</script>

<style lang="scss" scoped>
@use "sass:math";

$comment-padding: 10px;

.comment {
	display: flex;
	gap: 8px;
	padding: 5px $comment-padding;

	&__side {
		display: flex;
		align-items: flex-start;
		padding-top: 6px;
	}

	&__body {
		display: flex;
		flex-grow: 1;
		flex-direction: column;
		container-type: inline-size;
	}

	&__header {
		display: flex;
		align-items: center;
		min-height: 44px;
	}

	&__actions {
		margin-inline-start: $comment-padding !important;
	}

	&__author {
		overflow: hidden;
		white-space: nowrap;
		text-overflow: ellipsis;
		color: var(--color-text-maxcontrast);
	}

	&_loading,
	&__timestamp {
		margin-inline-start: auto;
		text-align: end;
		white-space: nowrap;
		color: var(--color-text-maxcontrast);
	}

	&__editor-group {
		position: relative;
	}

	&__editor-description {
		color: var(--color-text-maxcontrast);
		padding-block: var(--default-grid-baseline);
	}

	&__submit {
		position: absolute !important;
		bottom: 5px;
		inset-inline-end: 0;
	}

	&__message {
		white-space: pre-wrap;
		word-break: normal;
		max-height: 200px;
		overflow: auto;
		scrollbar-gutter: stable;
		scrollbar-width: thin;
		margin-top: -6px;
		&--expanded {
			max-height: none;
			overflow: visible;
		}
		:deep(img) {
			max-width: 100%;
			height: auto;
		}
	}
}

.rich-contenteditable__input {
	min-height: 44px;
	margin: 0;
	padding: $comment-padding;
}

</style>
